package com.hzlj.position.locate.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import com.fz.common.base.core.JsonUtils;
import com.fz.common.base.core.date.DateUtils;
import com.fz.common.base.dto.PageParamDTO;
import com.fz.common.base.dto.QueryPageResultDTO;
import com.fz.common.base.exception.PubError;
import com.fz.common.base.exception.PubException;
import com.fz.common.dict.annotation.DictConvert;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.hzlj.common.message.common.dto.message.MessageNoticeResultDTO;
import com.hzlj.common.message.common.dto.message.MessageNoticeUserDTO;
import com.hzlj.common.message.common.dto.message.MessageNotifyDTO;
import com.hzlj.common.message.common.util.MessageRestUtil;
import com.hzlj.position.config.common.dto.common.PositionCommonDTO;
import com.hzlj.position.config.config.PositionConfig;
import com.hzlj.position.config.service.NoticeConfigService;
import com.hzlj.position.locate.common.common.PositionSimpleDTO;
import com.hzlj.position.locate.common.dto.abnMissingJzryNotice.AbnMissingJzryNoticeAddDTO;
import com.hzlj.position.locate.common.dto.abnMissingJzryNotice.AbnMissingJzryNoticeExt;
import com.hzlj.position.locate.common.dto.abnMissingJzryNotice.AbnMissingJzryNoticeQueryParamDTO;
import com.hzlj.position.locate.common.dto.abnMissingJzryNotice.AbnMissingJzryNoticeQueryResultDTO;
import com.hzlj.position.locate.common.dto.positionNewestEffective.PositionNewestEffectiveSimpleResultDTO;
import com.hzlj.position.locate.common.enums.AbnMissingCheck;
import com.hzlj.position.locate.common.enums.AbnMissingCheckStatus;
import com.hzlj.position.locate.common.enums.AbnMissingStep;
import com.hzlj.position.locate.common.model.AbnMissingJzryNotice;
import com.hzlj.position.locate.converter.AbnMissingJzryNoticeConverter;
import com.hzlj.position.locate.dao.AbnMissingJzryDao;
import com.hzlj.position.locate.dao.AbnMissingJzryNoticeDao;
import com.hzlj.position.locate.service.AbnMissingJzryNoticeService;
import com.hzlj.position.locate.service.PositionNewestEffectiveService;
import com.hzlj.position.locate.service.PositionService;
import com.hzlj.sqjz.config.common.enums.HandleStatus;
import com.hzlj.sqjz.jz.common.dto.common.JzCommonQueryResultDTO;
import com.hzlj.sqjz.jz.service.JzJbxxService;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;

import static com.hzlj.position.config.config.IdConfig.POSITION_ID_GEN;
import static com.hzlj.position.locate.common.enums.AbnMissingStep.*;

/**
 * 定位分析-疑似失联人员-通知(AbnMissingJzryNotice)表服务接口
 *
 * @author makejava
 * @date 2024-06-25 14:40:44
 */
@Slf4j
@Service
public class AbnMissingJzryNoticeServiceImpl implements AbnMissingJzryNoticeService {
    @Resource
    private AbnMissingJzryNoticeDao abnMissingJzryNoticeDao;
    @Resource
    private JzJbxxService jzJbxxService;
    @Resource
    private NoticeConfigService noticeConfigService;
    @Resource
    private PositionService positionService;
    @Resource
    private PositionNewestEffectiveService positionNewestEffectiveService;
    @Resource
    private AbnMissingJzryDao abnMissingJzryDao;
    @Resource
    private PositionConfig positionConfig;

    private static final int reportMinutes = 30;

    /**
     * 通过id查询单条数据
     */
    @Override
    public AbnMissingJzryNoticeQueryResultDTO getAbnMissingJzryNoticeById(String id) {
        AbnMissingJzryNotice abnMissingJzryNotice = this.abnMissingJzryNoticeDao.getById(id);
        if (Objects.isNull(abnMissingJzryNotice)) {
            throw new PubException(PubError.NO_OBJECT_FOUND);
        }
        return AbnMissingJzryNoticeConverter.from(abnMissingJzryNotice);
    }

    @Override
    public void saveAbnMissingJzryNotice(List<AbnMissingJzryNoticeAddDTO> notices) {
        Date date = new Date();
        List<AbnMissingJzryNotice> adds = notices.stream().flatMap(e -> {
            return Lists.newArrayList(
                    //通知矫正人员
                    convert(STEP1, date, true, e),
                    //通知家属
                    convert(STEP2, DateUtil.offsetMinute(date, reportMinutes), true, e),
                    //通知工作人员相关结果
                    convert(AbnMissingStep.STEP3, DateUtil.offsetMinute(date, reportMinutes * 2), true, e)
            ).stream();
        }).collect(Collectors.toList());

        List<List<AbnMissingJzryNotice>> split = CollectionUtil.split(adds, 500);
        for (List<AbnMissingJzryNotice> addsItems : split) {
            this.abnMissingJzryNoticeDao.saveBatch(addsItems);
        }
    }

    @Override
    public void notice(AbnMissingStep step) {
        Date noticeTime = new Date();
        AbnMissingJzryNoticeQueryParamDTO params = new AbnMissingJzryNoticeQueryParamDTO()
                .setBelongDay(DateUtil.beginOfDay(noticeTime))
                .setCanNotice(true)
                .setNoticeTimeLte(noticeTime)
                .setNoticeStep(step.getStep());

        List<AbnMissingJzryNoticeQueryResultDTO> list = this.listAbnMissingJzryNotice(params);
        if (ObjectUtil.isEmpty(list)) {
            return;
        }

        log.info("【疑似失联人员】:发送通知,step={},size={}", step, list.size());
        CountDownLatch countDownLatch = new CountDownLatch(list.size());
        ExecutorService executor = Executors.newFixedThreadPool(positionConfig.getAbn().getMissingNoticeThread());
        for (AbnMissingJzryNoticeQueryResultDTO abn : list) {
            executor.execute(() -> {
                try {
                    this.notice(abn);
                } catch (Exception e) {
                    log.error("【疑似失联人员】:发送通知失败,step={},abn={}", step, JsonUtils.obj2json(abn), e);
                } finally {
                    countDownLatch.countDown();
                }
            });

        }
        try {
            countDownLatch.await();
        } catch (Exception e) {
            log.error("【疑似失联人员】:发送通知失败(await),step={}", step, e);
        }
    }

    @Override
    public void updateNoticeResult(MessageNotifyDTO notify) {
        String serviceId = notify.getServiceId();
        if (ObjectUtil.isEmpty(serviceId)) {
            return;
        }
        AbnMissingJzryNotice notice = this.abnMissingJzryNoticeDao.getById(serviceId);
        AbnMissingJzryNoticeExt ext = notice.getExt();
        if (ext == null) {
            ext = new AbnMissingJzryNoticeExt();
        }
        ext.setMessage(new AbnMissingJzryNoticeExt.MessageDTO()
                .setMessageId(notify.getMessageId() == null ? null : notify.getMessageId().toString())
                .setAudio(notify.getExt().getAudio())
                .setAudioDuration(notify.getExt().getAudioDuration())
                .setMessageServiceId(notify.getServiceId())
                .setMsg(notify.getMsg())
        );

        AbnMissingJzryNotice update = new AbnMissingJzryNotice();
        update.setId(serviceId);
        update.setExt(ext);
        this.abnMissingJzryNoticeDao.updateById(update);
    }


    private void notice(AbnMissingJzryNoticeQueryResultDTO abn) {
        JzCommonQueryResultDTO jzJbxx = jzJbxxService.getJzCommonById(abn.getJzId());
        //参数
        Map<String, Object> params = Maps.newHashMap();
        params.put("jzryXm", jzJbxx.getXm());
        params.put("jgMc", jzJbxx.getJgMc());
        params.put("positionDay", DateUtil.format(abn.getBelongDay(), "yyyy年MM月dd日"));
        params.put("spotCheckTime", DateUtils.formatDate(new Date()));
        //不同的发送对象
        switch (AbnMissingStep.of(abn.getNoticeStep())) {
            case STEP1:
                noticeStep1(abn, params);
                break;
            case STEP2:
                noticeStep2(abn, params);
                break;
            case STEP3:
                noticeStep3(abn, params, jzJbxx);
                break;
            default:
                throw new PubException("疑似失联人员发送消息匹配步骤失败");
        }
    }

    /**
     * 步骤1：发送给矫正人员
     */
    private void noticeStep1(AbnMissingJzryNoticeQueryResultDTO abn,
                             Map<String, Object> params) {

        List<MessageNoticeResultDTO> notices = MessageRestUtil.noticeQueryUsers(
                "abn-missing-" + abn.getNoticeStep(),
                abn.getId(),
                params,
                MessageNoticeUserDTO.of(false, abn.getJzId()));
        abnMissingJzryDao.updateCheck(
                abn.getMissingId(),
                STEP1.getStep(),
                AbnMissingCheck.ING,
                AbnMissingCheckStatus.JZRY_WAIT,
                HandleStatus.NONE,
                null);
        //更新
        this.updateNoticeResult(abn, notices, null);
    }

    /**
     * 步骤2：发送给家属
     */
    private void noticeStep2(AbnMissingJzryNoticeQueryResultDTO abn,
                             Map<String, Object> params) {
        LatestPosition latestPosition = this.getLatestPosition(abn);
        //30分钟内没有上报
        if (!latestPosition.hasPosition) {
            List<MessageNoticeResultDTO> notices = MessageRestUtil.noticeQueryUsers(
                    "abn-missing-" + abn.getNoticeStep(),
                    abn.getId(),
                    params,
                    MessageNoticeUserDTO.ofFamily(abn.getJzId()));

            abnMissingJzryDao.updateCheck(
                    abn.getMissingId(),
                    STEP2.getStep(),
                    AbnMissingCheck.ING,
                    AbnMissingCheckStatus.JJLXR_WAIT,
                    HandleStatus.NONE,
                    null);
            //更新
            this.updateNoticeResult(abn, notices, null);
        } else {
            //删除本条记录
            this.abnMissingJzryNoticeDao.removeById(abn.getId());
            //修改给工作人员的发送时间
            abnMissingJzryNoticeDao.updateNoticeTimeByStep(abn.getMissingId(), AbnMissingStep.STEP3.getStep(), abn.getNoticeTime());
        }

    }

    @Data
    @AllArgsConstructor
    private static class LatestPosition {
        private boolean hasPosition;
        private PositionNewestEffectiveSimpleResultDTO position;
    }


    /**
     * 步骤3：发送给工作人员
     */
    private void noticeStep3(AbnMissingJzryNoticeQueryResultDTO abn,
                             Map<String, Object> params,
                             JzCommonQueryResultDTO jzJbxx) {
        LatestPosition latestPosition = this.getLatestPosition(abn);
        boolean hasPosition = latestPosition.hasPosition;
        PositionNewestEffectiveSimpleResultDTO position = latestPosition.getPosition();

        //通知
        List<MessageNoticeResultDTO> notices = MessageRestUtil.noticeQueryUsers(
                "abn-missing-" + abn.getNoticeStep() + "-" + (hasPosition ? "1" : "2") + "-gzry",
                abn.getId(),
                params,
                MessageNoticeUserDTO.of(true, jzJbxx.getJgCode(), noticeConfigService.defaultMessageNoticeAuths(jzJbxx.getJgCode()))
        );
        AbnMissingCheckStatus checkStatus = AbnMissingCheckStatus.JJLXR_NOT_REPORT;
        Date reportTime = null;
        if (hasPosition) {
            AbnMissingJzryNoticeQueryResultDTO step1 = this.abnMissingJzryNoticeDao.getByStep(abn.getMissingId(), STEP1.getStep());
            checkStatus = DateUtil.offsetMinute(step1.getNoticeTime(), reportMinutes).compareTo(abn.getNoticeTime()) >= 0 ?
                    AbnMissingCheckStatus.JZRY_REPORT : AbnMissingCheckStatus.JJLXR_REPORT;
            reportTime = position.getPositionTime();
        }

        abnMissingJzryDao.updateCheck(
                abn.getMissingId(),
                STEP3.getStep(),
                hasPosition ? AbnMissingCheck.NOT_MISSING : AbnMissingCheck.MISSING,
                checkStatus,
                hasPosition ? HandleStatus.NONE : HandleStatus.WAIT,
                reportTime
        );

        //更新
        AbnMissingJzryNoticeExt ext = new AbnMissingJzryNoticeExt();
        ext.setHasPosition(hasPosition);
        if (ObjectUtil.isNotEmpty(position)) {
            ext.setPosition(new PositionSimpleDTO(
                    position.getPositionId(),
                    position.getLongitude(),
                    position.getLatitude(),
                    position.getPosition(),
                    position.getPositionTime()
            ));
        }
        this.updateNoticeResult(abn, notices, ext);
    }

    private LatestPosition getLatestPosition(AbnMissingJzryNoticeQueryResultDTO abn) {
        String jzId = abn.getJzId();
        Date startTime = DateUtil.offsetMinute(abn.getNoticeTime(), -reportMinutes);
        Date endTime = abn.getNoticeTime();
        List<PositionCommonDTO> positions = positionService.listPosition4AbnByJzId(jzId, startTime, endTime);
        if (ObjectUtil.isEmpty(positions)) {
            return new LatestPosition(false, positionNewestEffectiveService.getPositionNewestEffective(jzId));
        }
        PositionCommonDTO position = positions.get(positions.size() - 1);
        PositionNewestEffectiveSimpleResultDTO positionNewestEffective = BeanUtil.copyProperties(position, PositionNewestEffectiveSimpleResultDTO.class);
        positionNewestEffective.setPositionId(position.getId());
        return new LatestPosition(true, positionNewestEffective);
    }


    private void updateNoticeResult(AbnMissingJzryNoticeQueryResultDTO abn,
                                    List<MessageNoticeResultDTO> notices,
                                    AbnMissingJzryNoticeExt ext) {
        //结果
        MessageNoticeResultDTO prioritySuccess = MessageRestUtil.getPrioritySuccess(notices);

        AbnMissingJzryNotice update = new AbnMissingJzryNotice();
        update.setId(abn.getId());
        update.setNotice(prioritySuccess != null && !prioritySuccess.isFail());
        update.setNoticeMsg(prioritySuccess != null ? prioritySuccess.getStatusMsg() : "发送通知异常");
        update.setRealNoticeTime(prioritySuccess != null && prioritySuccess.getSendTime() != null ? DateUtil.parseDateTime(prioritySuccess.getSendTime()) : new Date());
        update.setExt(ext);
        update.setCanNotice(false);

        this.abnMissingJzryNoticeDao.updateById(update);
    }

    /**
     * 查询列表：不分页
     */
    @Override
    @DictConvert
    public List<AbnMissingJzryNoticeQueryResultDTO> listAbnMissingJzryNotice(AbnMissingJzryNoticeQueryParamDTO dto) {
        return this.abnMissingJzryNoticeDao.listAbnMissingJzryNotice(dto);
    }

    /**
     * 查询列表：分页
     */
    @Override
    @DictConvert
    public QueryPageResultDTO<AbnMissingJzryNoticeQueryResultDTO> pageAbnMissingJzryNotice(AbnMissingJzryNoticeQueryParamDTO dto, PageParamDTO page) {
        return this.abnMissingJzryNoticeDao.pageAbnMissingJzryNotice(dto, page);
    }

    private AbnMissingJzryNotice convert(AbnMissingStep step,
                                         Date noticeTime,
                                         Boolean canNotice,
                                         AbnMissingJzryNoticeAddDTO dto) {
        AbnMissingJzryNotice add = BeanUtil.copyProperties(dto, AbnMissingJzryNotice.class);
        add.setId(POSITION_ID_GEN.nextIdStr());
        add.setNoticeStep(step.getStep());
        add.setNoticeTime(noticeTime);
        add.setCanNotice(canNotice);
        return add;
    }
}
